/**
 * Created by ${PRODUCT_NAME}.
 * User: skylerparr
 * Date: 12/12/10
 * Time: 9:30 PM
 * To change this template use File | Settings | File Templates.
 */
package com.thoughtorigin.meta {
import com.thoughtorigin.module.ModuleLoader;
import com.thoughtorigin.reflect.MetaDataItem;
import com.thoughtorigin.reflect.ObjectMetaData;

import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;

/**
 * works for functions and properties
 */
public class FunctionMetaWorker implements MetadataWorker{

    private var _metaTypes: Dictionary;
    private var _metaItems: Array;
    private var _callback: Function;
    private var _object: Object;
    private var _metaDataIndex: int;
    private var _metaData: ObjectMetaData;
    private var _metaOperator: MetaOperator;
    private var _moduleLoader: ModuleLoader;

    public function FunctionMetaWorker() {
        _metaTypes = new Dictionary();
    }

    /**
     * this implementation only works on functions and properties.
     *
     * @param object
     * @param metaData
     * @param callback
     */
    public function operate(object:Object, metaData:ObjectMetaData, callback:Function): void {
        if(!metaData) {
            if(callback != null) {
                callback.call(this);
            }
            return;
        }

        _object = object;
        _callback = callback;
        _metaData = metaData;
        _metaItems = [];
        for(var key: String in metaData.functionMetaData) {
            var arr: Array = metaData.functionMetaData[key];
            for each(var metaItem: MetaDataItem in arr) {
                if(_metaTypes[metaItem.metaDataName]) {
                    _metaItems.push(metaItem);
                }
            }
        }
        for(key in metaData.propertyMetaData) {
            arr = metaData.propertyMetaData[key];
            for each(metaItem in arr) {
                if(_metaTypes[metaItem.metaDataName]) {
                    _metaItems.push(metaItem);
                }
            }
        }

        _metaItems.sort(compareFunction);

        _metaDataIndex = _metaItems.length;
        operateItem();
    }

    private function compareFunction(a: MetaDataItem, b: MetaDataItem): int {
        var metaTypeA: MetaType = _metaTypes[a.metaDataName];
        var metaTypeB: MetaType = _metaTypes[b.metaDataName];
        if(metaTypeA.priority < metaTypeB.priority) return 1;
        if (metaTypeA.priority > metaTypeB.priority) return -1;
        return 0;
    }

    private function operateItem(): void {
        _metaDataIndex--;
        if(_metaDataIndex <= -1) {
            if(_callback != null) {
                _callback.call(this);
            }
            dispose();
            return;
        }

        var metaItem: MetaDataItem = _metaItems[_metaDataIndex];
        var metaType: MetaType = _metaTypes[metaItem.metaDataName];
        var className: String = getQualifiedClassName(metaType);
        var metaTypeClass: Class = _moduleLoader.getClassByName(className);
        _metaOperator.inject(metaTypeClass, null, function(metaType: MetaType): void {
            if(metaType) {
                metaType.decorate(_object, metaItem, operateItem);
            } else {
                operateItem();
            }
        })
    }

    /**
     *
     * @param metaName
     * @param metaType
     */
    public function addMetaType(metaName: String, metaType: MetaType): void {
        _metaTypes[metaName] = metaType;
    }

    public function get metaTypes(): Dictionary {
        return _metaTypes;
    }

    [Inject]
    public function set metaOperator(value:MetaOperator):void {
        _metaOperator = value;
    }

    [Inject]
    public function set moduleLoader(value:ModuleLoader):void {
        _moduleLoader = value;
    }

    public function dispose(... args): void {
        _metaData = null;
        _metaItems = null;
        _callback = null;
        _object = null;
        _metaTypes = null;
        _metaOperator = null;
    }
}
}

